package com.sneakxy.cloudbase.platform.config;

import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import com.sneakxy.cloudbase.platform.cache.ShiroRedisCacheManager;
import com.sneakxy.cloudbase.platform.filters.CloudBasePermissionsAuthorizationFilter;
import com.sneakxy.cloudbase.platform.interceptor.CloudBaseAopAllianceAnnotationsAuthorizingMethodInterceptor;
import com.sneakxy.cloudbase.platform.interceptor.CloudBaseAuthorizationAttributeSourceAdvisor;
import com.sneakxy.cloudbase.platform.properties.SignatureProperties;
import com.sneakxy.cloudbase.platform.service.IShiroService;
import com.sneakxy.cloudbase.platform.service.PasswordCredentialsMatcher;
import com.sneakxy.cloudbase.platform.service.ShiroRealm;
import com.sneakxy.cloudbase.platform.service.ShiroService;
import com.sneakxy.cloudbase.platform.utils.web.signature.SignatureValidator;

public class ShiroConfig {
	
	@Value("${shiro.remember-me.cipher-key}")
	private String cipherKey;
	
	@Bean
	public SessionManager sessionManager() {
		return new ServletContainerSessionManager();
	}
	
	@Bean
	public ShiroRedisCacheManager shiroRedisCacheManager(RedisTemplate<Object, Object> redisTemplate) {
		return new ShiroRedisCacheManager(redisTemplate);
	}
	
	@Bean
	@ConfigurationProperties(prefix="shiro.realm")
	public ShiroRealm realm(ShiroRedisCacheManager shiroRedisCacheManager) {
		return new ShiroRealm(shiroRedisCacheManager, credentialsMatcher());
	}
	
	@Bean
	public org.apache.shiro.mgt.SecurityManager securityManager(ShiroRealm realm, SessionManager sessionManager) {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(realm);
		securityManager.setCacheManager(realm.getCacheManager());
		securityManager.setSessionManager(sessionManager);
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}
	
	@Bean
	@ConfigurationProperties(prefix="shiro.method")
	public MethodInvokingFactoryBean methodInvokingFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
		MethodInvokingFactoryBean factory = new MethodInvokingFactoryBean();
		factory.setArguments(securityManager);
		return factory;
	}
	
	@Bean
	public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
		ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
		factory.setSecurityManager(securityManager);
		factory.getFilterChainDefinitionMap().put("/doc.html", "perms[sys:dev:doc]");
		factory.getFilterChainDefinitionMap().put("/webjars/**", "perms[sys:dev:doc]");
		factory.getFilterChainDefinitionMap().put("/**", "anon");
		factory.getFilters().put("perms", new CloudBasePermissionsAuthorizationFilter());
		return factory;
	}
	
	@Bean
	public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}
	
	@Bean
	@DependsOn("lifecycleBeanPostProcessor")
	public DefaultAdvisorAutoProxyCreator shiroAdvisorAutoProxyCreator() {
		DefaultAdvisorAutoProxyCreator creator =  new DefaultAdvisorAutoProxyCreator();
		creator.setProxyTargetClass(true);
		return creator;
	}
	
	@Bean
	public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager, SignatureValidator signatureValidator) {
		CloudBaseAuthorizationAttributeSourceAdvisor shiroAdvisor = new CloudBaseAuthorizationAttributeSourceAdvisor();
		shiroAdvisor.setAdvice(new CloudBaseAopAllianceAnnotationsAuthorizingMethodInterceptor(signatureValidator));
		shiroAdvisor.setSecurityManager(securityManager);
		return shiroAdvisor;
	}
	
	@Bean
	@ConfigurationProperties(prefix="signature")
	public SignatureProperties signatureProperties() {
		return new SignatureProperties();
	}
	
	@Bean
	public SignatureValidator signatureValidator(SignatureProperties signatureProperties) {
		return new SignatureValidator(signatureProperties.getSecretKey(), signatureProperties.getTimeout());
	}
	
	@Bean
	public RememberMeManager rememberMeManager() {
		CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
		rememberMeManager.setCipherKey(org.apache.shiro.codec.Base64.decode(cipherKey));
		rememberMeManager.setCookie(rememberMeCookie());
		return rememberMeManager;
	}
	
	@Bean
	@ConfigurationProperties(prefix="shiro.cookie.remember")
	public Cookie rememberMeCookie() {
		return new SimpleCookie();
	}
	
	@Bean
	public CredentialsMatcher credentialsMatcher() {
		return new PasswordCredentialsMatcher(this.passwordEncoder());
	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	@Bean
	@ConditionalOnMissingBean
	public IShiroService shiroService() {
		return new ShiroService();
	}
	
}
